#include "ev_serial.h"
#include <QThread>
#include <QDateTime>
#include <QDebug>

void EvSerial::ev_parser_callback(const ev_parser_t *parser, const ev_packet_t *packet) {
    EvSerial *serialPort = static_cast<EvSerial*>(parser->arg);
    if (!packet || !serialPort) {
        return;
    }
    auto newPacket = std::make_shared<ev_packet_t>();
    *newPacket = *packet;
    if (packet->cmd < 0xFF00) {
        emit serialPort->packetReceived(newPacket);
    } else {
        emit serialPort->logReceived(newPacket);
    }
}

EvSerial::EvSerial(QObject *parent)
    : QObject(parent)
{
    m_serialPort = new QSerialPort(this);
    m_timeoutTimer = new QTimer(this);
    m_timeoutTimer->setSingleShot(true);
    m_timeoutTimer->setInterval(PACKET_TIMEOUT_MS);

    ev_parser_init(&m_parser, this, ev_parser_callback);

    connect(m_serialPort, &QSerialPort::readyRead, this, &EvSerial::onReadyRead);
    connect(m_serialPort, &QSerialPort::errorOccurred, this, &EvSerial::onError);
    connect(m_timeoutTimer, &QTimer::timeout, this, &EvSerial::onTimeout);
}

EvSerial::~EvSerial()
{
    close();
}

bool EvSerial::open(const QString &portName, int baudRate)
{
    if (m_serialPort->isOpen()) {
        return false;
    }
    m_serialPort->setPortName(portName);
    m_serialPort->setBaudRate(baudRate);
    m_serialPort->setDataBits(QSerialPort::Data8);
    m_serialPort->setParity(QSerialPort::NoParity);
    m_serialPort->setStopBits(QSerialPort::OneStop);
    m_serialPort->setFlowControl(QSerialPort::NoFlowControl);
    return m_serialPort->open(QIODevice::ReadWrite);
}

bool EvSerial::isOpen()
{
    return m_serialPort->isOpen();
}

void EvSerial::close()
{
    if (m_serialPort->isOpen()) {
        m_serialPort->close();
    }
    m_timeoutTimer->stop();

    QMutexLocker locker(&m_parserMutex);
    ev_parser_reset(&m_parser);
}

bool EvSerial::sendPacket(std::shared_ptr<ev_packet_t> packet)
{
    if (!m_serialPort->isOpen()) {
        return false;
    }

    uint8_t buffer[EV_PACKET_MAX_LEN];
    uint32_t bufferLength = 0;

    if (ev_packet_make(packet.get(), buffer, &bufferLength) != 0) {
        return false;
    }

    qint64 bytesWritten = m_serialPort->write(reinterpret_cast<const char*>(buffer), bufferLength);
    if (bytesWritten != (qint64)bufferLength) {
        return false;
    }

    if (!m_serialPort->waitForBytesWritten(1000)) {
        return false;
    }

    return bytesWritten == static_cast<qint64>(bufferLength);
}

/* 
 * 本函数可能跨线程调用
 * 故不能使用EvSerial的任何API及成员变量，避免出错
 * 仅能使用EvSerial发出的信号
 */
std::shared_ptr<ev_packet_t> EvSerial::safeRecvPacket(EvSerial *serial, int timeout_ms)
{
    if (timeout_ms <= 0) {
        return nullptr;
    }

    QEventLoop loop;
    QTimer timer;
    timer.setSingleShot(true);
    std::shared_ptr<ev_packet_t> recv_pkt = nullptr;
    connect(serial, &EvSerial::packetReceived, &loop,
        [&loop, &recv_pkt](std::shared_ptr<ev_packet_t> packet){
            recv_pkt = packet;
            loop.quit();
        }
    );
    connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);

    timer.start(timeout_ms);
    loop.exec();
    return recv_pkt;
}

/* 
 * 本函数可能跨线程调用
 * 故不能直接使用EvSerial的任何API及成员变量，避免出错
 */
bool EvSerial::safeSendPacket(EvSerial *serial, std::shared_ptr<ev_packet_t> packet)
{
    bool result = false;
    QMetaObject::invokeMethod(serial, "sendPacket",
                                  Qt::BlockingQueuedConnection,
                                  Q_RETURN_ARG(bool, result),
                                  Q_ARG(std::shared_ptr<ev_packet_t>, packet));
    return result;
}

void EvSerial::onReadyRead()
{
    if (!m_serialPort->isOpen()) {
        return;
    }

    QByteArray data = m_serialPort->readAll();
    if (data.isEmpty()) {
        return;
    }
    QMutexLocker locker(&m_parserMutex);
    ev_parser_feed(&m_parser, reinterpret_cast<const uint8_t*>(data.constData()), data.size());
    // m_lastByteTime = QDateTime::currentMSecsSinceEpoch();
    m_timeoutTimer->start(PACKET_TIMEOUT_MS);
}

void EvSerial::onTimeout()
{
    QMutexLocker locker(&m_parserMutex);
    if (m_parser.offset != 0) {
        QByteArray dataArray(reinterpret_cast<const char*>(m_parser.buf), m_parser.offset);
        QString hexData = dataArray.toHex(' ').toUpper();
        qDebug() << "Serial drop data: " << hexData;
    }
    ev_parser_reset(&m_parser);
    // auto timeout_ms =  QDateTime::currentMSecsSinceEpoch() - m_lastByteTime;
    // qDebug() << "timeout : " << timeout_ms << " ms, reset Receiver!!";
}

void EvSerial::onError(QSerialPort::SerialPortError error)
{
    qDebug() << QString("串口事件: code(%1), 信息(%2)")
                    .arg((int)error)
                    .arg(m_serialPort->errorString()) ;
}
